from numpy import *
from random import randint
from math import exp

class Hopfield:
	T = 0.20
	Estocastic = False
	
	def __init__(self, size, patrones):
		self.patrones = patrones
		self.size = size
		self.W = matrix(zeros([size,size]))
		self.b = matrix(zeros(size))
		for x in patrones:
			self.W += x*x.T
		self.W = self.W / size

	def ActualizarNeurona(self, i):
		if self.Estocastic:
			dE = (self.W[i,:]*self.S).item(0)
			if -dE/self.T > 700:
				return -1
			pi = 1 / (1 + exp(-dE/self.T))
			return sign(pi - 0.5)
		else:
			return sign(self.W[i,:]*self.S + self.b[0,i]).item(0)

	def Energia(self):
		return -(self.S.T*self.W*self.S).item(0)

	def EvaluarAsincronicamente(self, S):
		self.S = S.copy()
		old_e = 0
		new_e = self.Energia()
		actualizaciones=0
		repeticiones=0
		energia=[]
#		while repeticiones<10*self.size and actualizaciones<self.size*200:
		while actualizaciones < self.size*200:
#			print new_e
			energia.append(new_e)
			i = random.randint(0,self.size-1)
			self.S[i] = self.ActualizarNeurona(i)
			old_e = new_e
			new_e = self.Energia()
			actualizaciones += 1
			if old_e==new_e:
				repeticiones+=1
			else:
				repeticiones=0
		energia.append(new_e)
		return energia, self.S

	def EvaluarSincronicamente(self, S):
		self.S = S.copy()
		old_e = 0
		new_e = self.Energia()
		actualizaciones=0
		repeticiones=0
		energia=[]
#		while old_e!=new_e and actualizaciones < 200/4:
		while actualizaciones < 200:
#			print old_e
			energia.append(new_e)
			new_S = matrix(zeros([self.size,1]))
			for i in range(self.size):
				new_S[i] = self.ActualizarNeurona(i)
			self.S = new_S.copy()
			old_e = new_e
			new_e = self.Energia()
			actualizaciones += 1
			if old_e==new_e:
				repeticiones+=1
			else:
				repeticiones=0
		energia.append(new_e)
		return energia, self.S

	def Evaluar(self,S,sync=False, estocastic=False):
		res = 0,0
		if estocastic:
			self.Estocastic = True
		if sync:
			res = self.EvaluarSincronicamente(S)
		else:
			res = self.EvaluarAsincronicamente(S)
		if estocastic:
			self.Estocastic = False
		return res
